<template>
  <div class="node-display" :class="{ 'node-active': oBus.activeNode === node }"
    :style="{ left: `${node.x}px`, top: `${node.y}px`, }" @click.stop="onClick" @contextmenu.stop="onContextMenu">

    <ContextMenu title="节点" :context="node" :menu-items="contextMenuItem" />

    <div v-if="node && !headLess" px2 c-white flex items-center justify-between :style="{
      background: getBgByType(node.prototype.type),
    }" @mousedown="_hmd" @mouseup="_hmu" 头部>
      <GraphSlotDisplay v-if="hasProcessIn(node)" :slot="node.slots.filter(s => isProcessIn(s))[0]" />
      <div v-else w="16px" h="12px"></div>

      <div select-none v-if="node.prototype.type !== PNodeType.Getter">{{ title }}</div>

      <GraphSlotDisplay v-if="hasProcessOut(node)" :slot="node.slots.filter(s => isProcessOut(s))[0]" />
      <div v-else w="16px" h="12px"></div>
    </div>

    <div px2 v-if="node.getDataSlots().length" flex @mousedown="_bmd" @mouseup="_bmu" :style="{
      background: headLess ? getBgByType(node.prototype.type) : '',
      color: headLess ? 'white' : 'var(--color-body)',
    }" 主体>
      <div 进数据 v-if="node.getInDataSlots().length" flex flex-col justify-center>
        <GraphSlotDisplay v-for="item in node.getRequiredInDataSlots()" :slot="item" />
        <div flex justify-center hover-bg-gray-5 @click="toggleOptional" v-if="node.getOptionalInDataSlots().length">
          <ElIcon v-if="!showOptional">
            <ElIconCaretBottom c-gray />
          </ElIcon>
          <ElIcon v-else mb1>
            <ElIconCaretTop />
          </ElIcon>
        </div>
        <GraphSlotDisplay v-if="showOptional" v-for="item in node.getOptionalInDataSlots()" :slot="item" />
      </div>

      <div 分割 mx-2 flex items-center>
        {{ headLess ? title : '' }}
      </div>

      <div 出方向 flex flex-col justify-center>
        <GraphSlotDisplay v-if="node.getExtraOutProcessSlots().length" v-for="item in node.getExtraOutProcessSlots()"
          :slot="item" />
        <GraphSlotDisplay v-if="node.getOutDataSlots().length" v-for="item in node.getOutDataSlots()" :slot="item" />
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
import type { INode } from '~/models/graph/node/instance';
import { ElInput } from 'element-plus';
import { PSlot } from '~/models/graph/slot/prototype';
import { PSlotType } from "~/models/graph/slot/type";
import { PNodeType } from '~/models/graph/node/type';
import type { ISlot } from '~/models/graph/slot/instance';
import { MathPoint } from '~/models/Math/MathPoint';
import { oBus } from '~/models/graph/editorbus';
import { oGraph } from '~/models/graph/graph';

const props = defineProps<{
  node: INode;
}>()

const title = computed(() => {
  const tArr = props.node.typeTemplates
  if (tArr.length) {
    return `${props.node.prototype.title}<${tArr.map(x => x.name).join(', ')}>`
  }
  else {
    return props.node.prototype.title
  }
})

const headLess = computed(() => {
  return [
    PNodeType.Expression,
    PNodeType.Getter,
    PNodeType.PureFunc,
  ].includes(props.node.prototype.type)
})

function isProcessIn(s: ISlot) {
  return s.prototype.direction === 'in'
    && s.prototype.type === PSlotType.Process
    && s.prototype.name === '_in'
}

function hasProcessIn(n: INode) {
  return !!(n.slots.filter(s => isProcessIn(s)).length)
}

function isProcessOut(s: ISlot) {
  return s.prototype.direction === 'out'
    && s.prototype.type === PSlotType.Process
    && s.prototype.name === '_out'
}

function hasProcessOut(n: INode) {
  return !!(n.slots.filter(s => isProcessOut(s)).length)
}

const paramsList = computed(() => {
  // return paramListOfAtom.get(props.atom.type)
})
// const params = ref([])

onMounted(() => {
  let expanded = false
  props.node.getOptionalInDataSlots().forEach(slot => {
    const caseWritten = (
      slot.prototype.defaultValue !== undefined
      && slot.data != slot.prototype.defaultValue
    )
    const caseLinked = slot.peer !== undefined
    if (caseWritten || caseLinked) {
      console.log('expand', slot, slot.data, slot.prototype.defaultValue);

      expanded = true
    }
  })
  if (expanded) {
    showOptional.value = true
  }
})

/**
 * 
 * 节点移动相关的代码：开始
 * 
 */

const offsetOfEventClientToXY = ref(new MathPoint())

function onMoving(ev: MouseEvent) {
  props.node.x = ev.clientX + offsetOfEventClientToXY.value.x
  props.node.y = ev.clientY + offsetOfEventClientToXY.value.y
}

function onStartMove(ev: MouseEvent) {
  // 屏蔽触摸板的右键按下
  if (ev.button === 2) {
    return
  }
  // 计算出 鼠标的 client 位置 和 XY 的 偏移量
  offsetOfEventClientToXY.value.x = props.node.x - ev.clientX
  offsetOfEventClientToXY.value.y = props.node.y - ev.clientY
  window.addEventListener('mousemove', onMoving)
}

function onEndMove(ev: MouseEvent) {
  window.removeEventListener('mousemove', onMoving)
}

function _hmd(ev: MouseEvent) { if (!headLess.value) { onStartMove(ev) } }
function _hmu(ev: MouseEvent) { if (!headLess.value) { onEndMove(ev) } }
function _bmd(ev: MouseEvent) { if (headLess.value) { onStartMove(ev) } }
function _bmu(ev: MouseEvent) { if (headLess.value) { onEndMove(ev) } }

/**
 * 
 * 节点移动相关的代码：结束
 * 
 */

/**
 * 节点的选中
 */
function onClick() {
  oBus.value.activeNode = props.node
}


/**
 * 可选参数相关的
 */

const showOptional = ref(false)
function toggleOptional() {
  showOptional.value = !showOptional.value

  // 关闭：恢复默认值
  if (!showOptional.value) {
    props.node.getOptionalInDataSlots().forEach(slot => {
      slot.disconnect()
      slot.data = slot.prototype.defaultValue
    })
  }
}

/**
 * 右键菜单相关
 */

const isContextMenuShown = ref(false)
const menuPosition = ref(new MathPoint())

function showContextMenu(position: MathPoint) {
  menuPosition.value = position
  isContextMenuShown.value = true
}

function onContextMenu(e: MouseEvent) {
  oBus.value.activeNode = props.node
  showContextMenu(new MathPoint(e.clientX, e.clientY))
  e.preventDefault()
}

const contextMenuItem = computed(() => {
  const allItems = [
    {
      icon: ElIconDelete,
      title: '删除节点',
      action: () => {
        oBus.value.deleteActiveNode()
        return true
      },
    }
  ]
  const removeItemByTitle = (title: string) => {
    remove(allItems, x => x.title === title)
  }
  console.log();

  if (['Start', 'End'].includes(props.node.prototype.name)) {
    removeItemByTitle('删除节点')
    allItems.push({
      icon: ElIconEdit,
      title: '修改声明',
      action: () => {
        oBus.value.editParam(oGraph.value!)
        return true
      }
    })
  }
  return allItems
})
</script>

<style lang="scss" scoped>
.node-display {
  border-radius: 0.25rem;
  overflow: hidden;
  font-size: 12px;
  padding: 2px;
  border: 1px solid transparent;
  box-shadow: var(--shadow-convex);
  background: var(--bg-body);
  position: absolute;
}

.node-active {
  border: 1px solid orangered;
  box-shadow: 0 0 5px 1px #ffffff80;
}
</style>